using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;

#pragma warning disable 0067

namespace VisualPureBasicIDE.FormDesigner.Implementations
{
    /// <summary>
    /// The designer host implementation
    /// </summary>
    public class DesignerHost : IDesignerHost, IContainer, IComponentChangeService, IExtenderProviderService,
                                IDesignerEventService
    {
        // Root component
        // Service container
        private IServiceContainer _serviceContainer;
        // Site-name to site mapping
        private IDictionary _sites;
        // Component to designer mapping
        private IDictionary _designers;
        // Extender provider list
        private IList _extenderProviders;
        // Transaction list

        public Designer Parent;

        public DesignerHost(IServiceProvider parentProvider, Designer parent)
        {
            // Append to the parentProvider...
            _serviceContainer = new ServiceContainer(parentProvider);

            // Site name to ISite mapping
            _sites = new Hashtable();

            // Component to designer mapping
            _designers = new Hashtable();

            // List of extender providers
            _extenderProviders = new ArrayList();

            // Create transaction stack
            Transactions = new Stack();

            // Services
            Parent = parent;
            _serviceContainer.AddService(typeof (IDesignerHost), this);
            _serviceContainer.AddService(typeof (IContainer), this);
            _serviceContainer.AddService(typeof (IComponentChangeService), this);
            _serviceContainer.AddService(typeof (IExtenderProviderService), this);
            _serviceContainer.AddService(typeof (IDesignerEventService), this);
            _serviceContainer.AddService(typeof (INameCreationService), new NameCreationService(this));
            _serviceContainer.AddService(typeof (ISelectionService), new SelectionService(this));
            _serviceContainer.AddService(typeof (IMenuCommandService), new MenuCommandService(this));
            _serviceContainer.AddService(typeof (ITypeDescriptorFilterService),
                                         new TypeDescriptorFilterService(this));
            _serviceContainer.AddService(typeof (IEventBindingService), new EventBindingService(parent));
        }

        #region IDesignerHost Members

        public IContainer Container
        {
            get { return this; }
        }

        public event EventHandler TransactionOpening;

        public event EventHandler TransactionOpened;

        public event EventHandler LoadComplete;

        public IDesigner GetDesigner(IComponent component)
        {
            return _designers[component] as IDesigner;
        }

        public event EventHandler Activated;

        public event EventHandler Deactivated;

        public event DesignerTransactionCloseEventHandler TransactionClosed;

        public bool Loading
        {
            get { return false; }
        }

        public IComponent CreateComponent(Type componentClass, string name)
        {
            var newComponent = Activator.CreateInstance(componentClass) as IComponent;
            Add(newComponent, name);

            return newComponent;
        }

        IComponent IDesignerHost.CreateComponent(Type componentClass)
        {
            return CreateComponent(componentClass, null);
        }

        public bool InTransaction
        {
            get
            {
                // We are in a transaction if the stack has something in it
                return (Transactions.Count > 0);
            }
        }

        public string TransactionDescription
        {
            get
            {
                DesignerTransaction designerTransaction = null;

                if (InTransaction)
                    designerTransaction = (DesignerTransaction) Transactions.Peek();

                return (designerTransaction == null) ? null : designerTransaction.Description;
            }
        }

        public System.ComponentModel.Design.DesignerTransaction CreateTransaction(string description)
        {
            return new DesignerTransaction(this, description);
        }

        System.ComponentModel.Design.DesignerTransaction IDesignerHost.CreateTransaction()
        {
            return CreateTransaction("");
        }

        internal Stack Transactions { get; private set; }

        internal void OnTransactionOpened(EventArgs e)
        {
            if (TransactionOpened != null)
                try
                {
                    TransactionOpened(this, e);
                }
                catch
                {
                }
        }

        internal void OnTransactionOpening(EventArgs e)
        {
            if (TransactionOpening != null)
                try
                {
                    TransactionOpening(this, e);
                }
                catch
                {
                }
        }

        internal void OnTransactionClosed(bool commit)
        {
            if (TransactionClosed != null)
            {
                var e = new DesignerTransactionCloseEventArgs(commit, true);

                try
                {
                    TransactionClosed(this, e);
                }
                catch
                {
                }
            }
        }

        internal void OnTransactionClosing(bool commit)
        {
            if (TransactionClosing != null)
            {
                var e = new DesignerTransactionCloseEventArgs(commit, true);

                try
                {
                    TransactionClosing(this, e);
                }
                catch
                {
                }
            }
        }

        public void DestroyComponent(IComponent component)
        {
            var designerTransaction = CreateTransaction("Destroying Component ");

            try
            {
                Remove(component);
            }
            finally
            {
                if (designerTransaction != null)
                    designerTransaction.Commit();
            }
        }

        public void Activate()
        {
            try
            {
                if (Activated != null)
                {
                    Activated(this, EventArgs.Empty);
                }
            }
            catch
            {
            }
        }

        public string RootComponentClassName
        {
            get { return RootComponent.GetType().Name; }
        }

        public Type GetType(string typeName)
        {
            var typeResolverService = (ITypeResolutionService) GetService(typeof (ITypeResolutionService));

            var type = typeResolverService != null ? typeResolverService.GetType(typeName) : Type.GetType(typeName);

            return type;
        }

        public event DesignerTransactionCloseEventHandler TransactionClosing;

        public IComponent RootComponent { get; private set; }

        #endregion

        #region IServiceContainer Members

        public void RemoveService(Type serviceType, bool promote)
        {
            _serviceContainer.RemoveService(serviceType, promote);
        }

        void IServiceContainer.RemoveService(Type serviceType)
        {
            _serviceContainer.RemoveService(serviceType);
        }

        public void AddService(Type serviceType, ServiceCreatorCallback callback, bool promote)
        {
            _serviceContainer.AddService(serviceType, callback, promote);
        }

        void IServiceContainer.AddService(Type serviceType, ServiceCreatorCallback callback)
        {
            _serviceContainer.AddService(serviceType, callback);
        }

        void IServiceContainer.AddService(Type serviceType, object serviceInstance, bool promote)
        {
            _serviceContainer.AddService(serviceType, serviceInstance, promote);
        }

        void IServiceContainer.AddService(Type serviceType, object serviceInstance)
        {
            _serviceContainer.AddService(serviceType, serviceInstance);
        }

        #endregion

        #region IServiceProvider Members

        public object GetService(Type serviceType)
        {
            //			if(_serviceContainer.GetService(serviceType)!=null)
            //			{
            //				Console.WriteLine("service requested "+serviceType.Name);
            //			}
            return _serviceContainer.GetService(serviceType);
        }

        #endregion

        #region IContainer Members

        public ComponentCollection Components
        {
            get { return new ComponentCollection(GetAllComponents()); }
        }

        public void Remove(IComponent component)
        {
            if (component == null)
            {
                throw new ArgumentException("component");
            }
            // Fire off changing and removing event
            var ce = new ComponentEventArgs(component);

            OnComponentChanging(component, null);

            try
            {
                if (ComponentRemoving != null)
                {
                    ComponentRemoving(this, ce);
                }
            }
            catch
            {
                // Don't throw here
            }

            // Make sure we own the component
            if (component.Site != null && component.Site.Container == this)
            {
                // Remove from extender provider list
                if (component is IExtenderProvider)
                {
                    var extenderProvider = (IExtenderProviderService) GetService(typeof (IExtenderProviderService));
                    if (extenderProvider != null)
                    {
                        extenderProvider.RemoveExtenderProvider((IExtenderProvider) component);
                    }
                }
                // Remove the site
                _sites.Remove(component.Site.Name);
                // Dispose the designer
                var designer = _designers[component] as IDesigner;
                if (designer != null)
                {
                    designer.Dispose();
                    // Get rid of the designer from the list
                    _designers.Remove(component);
                }

                // Fire off removed event
                try
                {
                    if (ComponentRemoved != null)
                    {
                        ComponentRemoved(this, ce);
                    }
                    OnComponentChanged(component, null, null, null);
                }
                catch
                {
                    // Don't throw here
                }

                // Breakdown component, container, and site relationship
                component.Site = null;

                // Now dispose the of the component too
                component.Dispose();
            }
        }

        public void Add(IComponent component, string name)
        {
            // We have to have a component
            if (component == null)
            {
                throw new ArgumentException("component");
            }

            // If we dont have a name, create one
            if (name == null || name.Trim().Length == 0)
            {
                // We need the naming service
                var nameCreationService = GetService(typeof (INameCreationService)) as INameCreationService;

                if (nameCreationService == null)
                    throw new Exception("Failed to get INameCreationService.");

                name = nameCreationService.CreateName(this, component.GetType());
            }

            // If we own the component and the name has changed
            // we just rename the component
            if (component.Site != null && component.Site.Container == this &&
                name != null && string.Compare(name, component.Site.Name, true) != 0)
            {
                // Name validation and component changing/changed events
                // are fired in the Site.Name property so we don't have 
                // to do it here...
                component.Site.Name = name;

                // Bail out
                return;
            }
            // Create a site for the component
            ISite site = new Site(component, name, this);
            // Create component-site association
            component.Site = site;
            // The container-component association was established when 
            // we created the site through site.host.
            // we need to fire adding/added events. Create a component event args 
            // for the component we are adding.

            var evtArgs = new ComponentEventArgs(component);

            // Fire off adding event
            if (ComponentAdding != null)
            {
                try
                {
                    ComponentAdding(this, evtArgs);
                }
                catch
                {
                }
            }

            // If this is the root component
            IDesigner designer;
            if (RootComponent == null)
            {
                // Set the root component
                RootComponent = component;
                // Create the root designer
                designer = TypeDescriptor.CreateDesigner(component, typeof (IRootDesigner));
            }
            else
            {
                designer = TypeDescriptor.CreateDesigner(component, typeof (IDesigner));
            }
            if (designer != null)
            {
                // Add the designer to the list
                _designers.Add(component, designer);
                // Initialize the designer
                designer.Initialize(component);
            }
            // Add to container component list
            _sites.Add(site.Name, site);
            // Now fire off added event
            if (ComponentAdded != null)
            {
                try
                {
                    ComponentAdded(this, evtArgs);
                }
                catch
                {
                }
            }
        }

        void IContainer.Add(IComponent component)
        {
            Add(component, null);
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            // Dispose all designers
            if (_designers != null && _designers.Count > 0)
            {
                foreach (IDesigner designer in _designers.Values)
                {
                    try
                    {
                        designer.Dispose();
                    }
                    catch
                    {
                    }
                }
            }
            // Dispose all components
            foreach (IComponent icomp in _sites.Values)
            {
                try
                {
                    icomp.Dispose();
                }
                catch
                {
                }
            }

            RootComponent = null;
            _serviceContainer = null;
            _sites = null;
            _designers = null;
            _extenderProviders = null;
            Transactions = null;
        }

        #endregion

        #region IComponentChangeService Members

        public event ComponentEventHandler ComponentRemoving;

        public void OnComponentChanged(object component, MemberDescriptor member, object oldValue, object newValue)
        {
            if (ComponentChanged != null)
            {
                var ce = new ComponentChangedEventArgs(component, member, oldValue, newValue);
                try
                {
                    ComponentChanged(this, ce);
                }
                catch
                {
                }
            }
        }

        public void OnComponentChanging(object component, MemberDescriptor member)
        {
            if (ComponentChanging != null)
            {
                var ce = new ComponentChangingEventArgs(component, member);
                try
                {
                    ComponentChanging(this, ce);
                }
                catch
                {
                }
            }
        }

        internal void OnComponentRename(object component, string oldName, string newName)
        {
            if (ComponentRename != null)
            {
                try
                {
                    ComponentRename(this, new ComponentRenameEventArgs(component, oldName, newName));
                }
                catch
                {
                }
            }
        }

        public event ComponentEventHandler ComponentAdded;

        public event ComponentRenameEventHandler ComponentRename;

        public event ComponentEventHandler ComponentAdding;

        public event ComponentEventHandler ComponentRemoved;

        public event ComponentChangingEventHandler ComponentChanging;

        public event ComponentChangedEventHandler ComponentChanged;

        #endregion

        #region Private Helper Methods

        private IComponent[] GetAllComponents()
        {
            var components = new IComponent[_sites.Count];
            // Loop over the sites and get all the components
            var ie = _sites.Values.GetEnumerator();
            var count = 0;
            while (ie.MoveNext())
            {
                var site = ie.Current as ISite;
                if (site != null) components[count++] = site.Component;
            }
            return components;
        }

        #endregion

        #region IExtenderProviderService Members

        public void RemoveExtenderProvider(IExtenderProvider provider)
        {
            _extenderProviders.Remove(provider);
        }

        public void AddExtenderProvider(IExtenderProvider provider)
        {
            _extenderProviders.Add(provider);
        }

        #endregion

        #region IDesignerEventService Members

        public DesignerCollection Designers
        {
            get
            {
                // We just have one designer
                var designers = new IDesignerHost[] {this};
                return new DesignerCollection(designers);
            }
        }

        public event DesignerEventHandler DesignerDisposed;

        public IDesignerHost ActiveDesigner
        {
            get
            {
                // Always this designer
                return this;
            }
        }

        public event DesignerEventHandler DesignerCreated;

        public event ActiveDesignerEventHandler ActiveDesignerChanged;

        public event EventHandler SelectionChanged;

        #endregion
    }
}